# -*- coding: utf-8 -*-
from PyQt5 import QtCore
import time
import json
from PyQt5.QtCore import QThreadPool
import numpy as np

import constantValues as cv
import debugPrinter as dp

from lslReceiver import LSLReceiver
from edfSaver import EDFSaver
import os
import subprocess
from worker import Worker
from setting import SETTING
from dsp.dSPx import DSPx
from dsp.dspx2 import DSPx2

from udp_server import start_server, send_text_to_all, recv_next, set_on_message
from queue import Empty
import time
import struct

from lslSender import LSLSender

from data2lsl import DATATOLSL

from scipy import signal

with open('./task/task_markers.json', 'r') as file:
    markers = json.load(file)

d2lsl = DATATOLSL('dummy')

def on_msg(cid, data):
    # print("callback:", cid, data)
    # print("callback:", cid)
    d2lsl.new_data_pkg_binary_bci32(data)

class Controller():
    def __init__(self,mainwindow,curForm):

        self.mw = mainwindow
        # self.lsl = LSLSender('dummy')
        self.cf = curForm
        self.fs = EDFSaver('./data')
        self.dsp = DSPx(cv.CH_NUM)
        self.dsp2 = DSPx2(cv.FS, cv.CH_NUM)

        self.setting = SETTING()

        self.usr_config_file = os.path.join(os.path.dirname(__file__),"ui_config", "user_config.json")

        with open(self.usr_config_file, 'r') as file:
            self.usr_config_json = json.load(file)

        self.mw.evt_win.connect(self.win_evt)
        # self.mw.evt_win_data.connect(self.win_evt_data)

        self.packet_counter=0
        self.threadpool = QThreadPool()

        self.last_pkn=None
        
        self.evt_serial_dataConnected = False

        self.start_time = time.time()

        self.psyco_marker_counter = 0

        # self.proc_udp=None
        self.proc_task=None

        self.lslRcv = LSLReceiver()

        self.lslRcv.evt_lslRcv.connect(self.lslRcv_new_data)

        self.timer_init = QtCore.QTimer()
        self.timer_init.setInterval(500)
        self.timer_init.setSingleShot(True)
        self.timer_init.timeout.connect(self.init_timer_handler)
        self.timer_init.start()

        self.num_channels = 32


        self.sample_rate = 500
        self.QUALITY_FACTOR = 10
        self.notch_freq = 50

        nyquist = 0.5 * self.sample_rate
        freq = self.notch_freq / nyquist
        self.b_notch, self.a_notch = signal.iirnotch(freq, self.QUALITY_FACTOR)
        self.zi_notch = [signal.lfilter_zi(self.b_notch, self.a_notch) for _ in range(self.num_channels)]

        # self.timer_test = QtCore.QTimer()
        # self.timer_test.setInterval(2000)
        # self.timer_test.timeout.connect(self.test_timer_handler)
        # self.timer_test.start()

    # def __del__(self):
    #     print('del Controller')
        # self.init_some()

    def init_timer_handler(self):
        # pass
        worker = Worker(self.setup_udp,cv.EVT_WIN_BTN_START_RECORDING)
        worker.autoDelete()
        self.threadpool.start(worker)

        # if (self.new_recording()=='f'):
        #     return            

    def test_timer_handler(self):
        inlets = self.lslRcv.get_all_inlets()
        print('inlets num: '+str(len(inlets)))
        for inlet in inlets:
            print(inlet.name)

    def notch_filter(self, data, ch):
        """陷波"""
        filtered, self.zi_notch[ch] = signal.lfilter(self.b_notch, self.a_notch, data, zi=self.zi_notch[ch])
        return filtered


    def lslRcv_new_data(self,inlet_name,ts,arr):
        if (inlet_name=='mii_eeg'):            
            # self.fs.new_data('eeg',ts,arr)

            # print(arr.shape)            
            # arr = self.dsp.filter(arr)
            # # arr = self.dsp.filter_dummy(arr)            
            self.cf.deal_with_data_inlet(ts,arr)

        if (inlet_name=='bci32'):            
            self.fs.new_data('eeg',ts,arr)

            # arr = self.dsp.filter(arr)
            # # arr = self.dsp.filter_dummy(arr)            

            # print(arr.shape)            

            # groups_T = arr.T
            # tmp_arr = arr
            # for i in range(self.num_channels):
            #     tmp = arr[:,i]
            #     tmp2 = self.notch_filter(tmp, i)
            #     arr[:,i] = tmp2

            # print(arr.shape)            
            # print('------------')            

            arr = self.dsp2.filter(arr)

            self.cf.deal_with_data_inlet(ts,arr)

        if (inlet_name[:13] == 'psycho_marker'):

            if (arr[0] != markers['trial_end']) and (arr[0] != markers['begin']) :
                dp.dpt(' - - - ')
                dp.dpt(arr)
                # dp.dpt(ts)
                self.psyco_marker_counter = self.psyco_marker_counter+1
                dp.dpt('trials: '+ str(self.psyco_marker_counter))

                dp.dpt(' - - -  - - -')

            self.fs.new_data('mar',ts,arr)

            # if arr[0]==markers['trial_end']:
            #     self.psyco_marker_counter = self.psyco_marker_counter+1
            #     dp.dpt('trials: '+ str(self.psyco_marker_counter))

    def get_new_inlet(self,wanted_inlet_names):
        tmp = wanted_inlet_names
        for i in range(20):
            time.sleep(1)
            print('looking for marker_inlet...'+wanted_inlet_names[0]+'...'+str(i))
            self.lslRcv.get_additional_marker_inlet()
            self.lslRcv.get_additional_signal_inlet(wanted_inlet_names[0])
            for inlet in self.lslRcv.info_names:
                print(inlet)
                if inlet in tmp:
                    tmp.remove(inlet)
            if len(tmp)==0:
                return 's'
        return 'f'


    def stim_on_exit(self,s):
        dp.dpt(s)
        dp.dpt('some proc terminated')
        if s==cv.EVT_WIN_BTN_MI_TRAIN:
            self.proc_task=None
            self.lslRcv.delete_inlet(['psycho_marker_001'])
            self.stop_recording()

    # def setup_udp(self,s):
    #     print('setup_udp')
    #     self.proc_udp = subprocess.Popen(['python', 'udpRcver.py'])
    #     # wanted_inlet_names = ['mii_eeg']
    #     wanted_inlet_names = ['bci32']

    #     if (self.get_new_inlet(wanted_inlet_names)=='s'):
    #         self.proc_udp.wait()
    #     else:
    #         self.mw.log_info('lab streaming layer error...')
    #         self.proc_udp.terminate()
    #     self.stim_on_exit("stop recording.")



        # pass

    def setup_udp(self,s):
        # 1. 启动服务器（监听端口）
        start_server()
        # 2. 注册回调（可选）
        set_on_message(on_msg)

        try:
            cid, data = recv_next(timeout=5.0) # recv one meesage, to get the clint ip, then we can send message to it
            # print("received:", cid, data)
            print("received:",cid)
            # send_text_to_all("{\"cmd\":\"stop recording\"}")
            send_text_to_all(cv.WIFI_CMD_START_RECORDING)

        except Empty:
            print("no message within 5s")


    def run_task(self,s):
        print('setup_task_worker')
        if s==cv.EVT_WIN_BTN_MI_TRAIN:
            task_file = './task/mii_4c_train.py'
            wanted_marker_inlets = ['psycho_marker_001']
        elif s==cv.EVT_WIN_BTN_MI_TEST:
            pass

        self.proc_task = subprocess.Popen(['python', task_file])
        if (self.get_new_inlet(wanted_marker_inlets)=='s'):
            self.proc_task.wait()
        else:
            self.mw.log_info('lab streaming layer error...')
            self.proc_task.terminate()

        self.stim_on_exit(s)        

    def setup_task(self,s):
        worker = Worker(self.run_task,s)
        worker.autoDelete()
        self.threadpool.start(worker)


    def win_evt(self,s,s2):
        if s == cv.SERIAL_CMD_FALSH_LED:
            pass
        elif s == cv.EVT_WIN_BTN_CLICK_SETTING:
            self.setting.show()
        elif s == cv.EVT_WIN_QUIT:
            self.stop_recording()
            # if self.proc_udp is not None:
            #     self.proc_udp.terminate()

            send_text_to_all(cv.WIFI_CMD_STOP_RECORDING)


            if self.proc_task is not None:
                self.proc_task.terminate()
            # self.cf.close()
            self.cf.close_win()
            # del self.cf
            self.setting.close()
            with open(self.usr_config_file, "w") as outfile:
                json.dump(self.usr_config_json, outfile)    
            self.threadpool.waitForDone()
            dp.dpt("exit ... ")

        elif s==cv.EVT_WIN_BTN_START_RECORDING:
            if (self.new_recording()=='f'):
                dp.dpt('failed to new file, check...')            
        elif s==cv.EVT_WIN_BTN_STOP_RECORDING:
            self.stop_recording()

        elif s==cv.EVT_WIN_BTN_GENERATW_MODEL:
            pass
        elif s==cv.EVT_WIN_BTN_MI_TEST:
            pass
        elif s==cv.EVT_WIN_BTN_MI_TRAIN:
            self.stop_recording()
            if (self.new_recording()=='f'):
                dp.dpt('failed to new file, check...')            
            self.setup_task(s)

    def new_recording(self):
        a = self.fs.get_name()
        b = a.split('/')[-1]

        text, ok = self.mw.get_input_fileName(b)
        if ok and text:
            c = a[:a.rfind('/')+1] + text
            print(c)
            if (self.fs.make_path(c)):
                self.mw.show_error("this file has been exist!")
                return 'f'
            else:
                self.fs.setup(c,text)
                self.mw.set_start_recording_btn_disable()
                return 's'
        return 'f'

    def stop_recording(self):
        if self.fs.save_on == 1:
            self.mw.set_start_recording_btn_enable()
            self.fs.flush_data()



